package com.bizmda.bizsip.common;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * @author shizhengye
 */
@Slf4j
public class BizTools {
    public static final ThreadLocal<DelayAppServiceContext> delayAppServiceContextThreadLocal = new ThreadLocal<>();
    public static final ThreadLocal<BizMessage<JSONObject>> bizMessageThreadLocal = new ThreadLocal<>();
    public static final ThreadLocal<String> serviceIdThreadLocal = new ThreadLocal<>();
    public static final ThreadLocal<JSONObject> controlRuleThreadLocal = new ThreadLocal<>();
    static ExecutorService elExecutorService = ThreadUtil.newExecutor(Runtime.getRuntime().availableProcessors());

    private BizTools() {
        throw new IllegalStateException("Utility class");
    }

    public static BizMessage<JSONObject> buildJsonObjectMessage(BizMessage<?> bizMessage, Object data) {
        if (data instanceof JSONObject || data == null) {
            return BizMessage.buildSuccessMessage(bizMessage,(JSONObject) data);
        }
        else if (data instanceof List) {
            JSONArray jsonArray = JSONUtil.parseArray(data);
            JSONObject jsonObject = new JSONObject();
            jsonObject.set("array",jsonArray);
            return BizMessage.buildSuccessMessage(bizMessage,jsonObject);
        }
        else {
            JSONObject jsonObject = JSONUtil.parseObj(data);
            return BizMessage.buildSuccessMessage(bizMessage,jsonObject);
        }
    }

    public static <T> BizMessage<T> copyBizMessage(BizMessage<T> inMessage) {
        BizMessage<T> bizMessage = new BizMessage<>();
        BeanUtil.copyProperties(inMessage,bizMessage);
        if (inMessage.getData() instanceof JSONObject) {
            bizMessage.setData((T) new JSONObject(inMessage.getData()));
        }
        return bizMessage;
    }

    public static String getElStringResult(String express, Map<String,Object> variableMap) throws BizException {
        String result;
        Future<Object> future = elExecutorService.submit(new ElThread(express, variableMap, false));
        try {
            result = (String) future.get();
        } catch (InterruptedException e) {
            log.error("EL表达式计算被中断:"+express, e);
            Thread.currentThread().interrupt();
            return null;
        } catch (ExecutionException e) {
            throw new BizException(BizResultEnum.OTHER_EL_EXECUTE_ERROR, e,
                    StrFormatter.format("EL表达式计算出错:{}", express));
        }
        return result;
    }

    public static Boolean getElBooleanResult(String express, Map<String,Object> variableMap) throws BizException {
        Boolean result;
        Future<Object> future = elExecutorService.submit(new ElThread(express, variableMap, true));
        try {
            result = (Boolean) future.get();
        } catch (InterruptedException e) {
            log.error("EL表达式计算被中断:"+express, e);
            Thread.currentThread().interrupt();
            return false;
        } catch (ExecutionException e) {
            throw new BizException(BizResultEnum.OTHER_EL_EXECUTE_ERROR, e,
                    StrFormatter.format("EL表达式计算出错:{}", express));
        }
        return result;
    }

    private static Object convertJson2Bean(Object json, Type type) {
        Object beanObject;
        if (json instanceof JSONObject) {
            beanObject = JSONUtil.toBean((JSONObject) json, type,true);
        } else if (json instanceof JSONArray) {
            beanObject = JSONUtil.toBean((JSONArray) json, type,true);
        } else {
            beanObject = Convert.convert(type, json);
        }
        return beanObject;
    }

    public static JSONObject getParamtersTypesJsonObject(Method method, Object[] args) {
        JSONObject jsonObject = new JSONObject();
        Type[] types = method.getGenericParameterTypes();
        for (int i = 0;i<types.length;i++) {
            if (args[i] == null) {
                continue;
            }
            if (types[i] instanceof ParameterizedType) {
                // 方法中参数为泛型不处理，用方法中的泛型来强转
                continue;
            }
            if (types[i] instanceof Class) {
                // 传参和方法参数定义中有基本类型，不处理
                if (args[i].getClass().isPrimitive()) {
                    continue;
                }
                if (((Class)types[i]).isPrimitive()) {
                    continue;
                }
            }

            if (types[i] != args[i].getClass()) {
                 jsonObject.set(String.valueOf(i), args[i].getClass().getName());
            }
        }
        return jsonObject;
    }

    public static Object[] convertJsonObject2MethodParameters(Method method, Object json, JSONObject paramsTypes) throws BizException {
        int parameterCount = method.getParameterCount();
        Type[] parameterTypes = method.getGenericParameterTypes();
        if (paramsTypes != null) {
            for (Map.Entry<String,Object> entry: paramsTypes.entrySet()) {
                String key = entry.getKey();
                int index = Integer.parseInt(key);
                try {
                    parameterTypes[index] = Class.forName((String)entry.getValue());
                } catch (ClassNotFoundException e) {
                    throw new BizException(BizResultEnum.OTHER_CLASS_NOTFOUND);
                }
            }
        }
        Object[] parameterObjects = new Object[parameterCount];
        if (parameterCount == 0) {
            return parameterObjects;
        } else {
            if (!(json instanceof JSONArray)) {
                throw new BizException(BizResultEnum.OTHER_JAVA_API_PARAMETER_ERROR, "输入参数应为JSONArray类型");
            }
            JSONArray jsonParams = (JSONArray) json;
            if (jsonParams.size() < parameterCount) {
                throw new BizException(BizResultEnum.OTHER_JAVA_API_PARAMETER_ERROR, "输入参数小于约定参数个数");
            }
            for (int i = 0; i < parameterCount; i++) {
                parameterObjects[i] = convertJson2Bean(jsonParams.get(i), parameterTypes[i]);
            }
            return parameterObjects;
        }

    }

    public static Object methodReturnBean2Json(Object returnValue) {
        if (returnValue == null) {
            return null;
        } else if (returnValue instanceof List || returnValue instanceof Object[]) {
            return JSONUtil.parseArray(returnValue);
        } else if (ObjectUtil.isBasicType(returnValue) || returnValue instanceof String || returnValue instanceof BigDecimal) {
            return returnValue;
        } else {
            return JSONUtil.parseObj(returnValue);
        }
    }

    public static Object convertMethodReturnJson2Bean(Method method, Object result) {
        if (result == null) {
            return null;
        }
        Type returnType = method.getGenericReturnType();
        return Convert.convert(returnType,result);
    }

    public static byte[] getBytes(String str) throws BizException {
        try {
            return str.getBytes(BizConstant.DEFAULT_CHARSET_NAME);
        } catch (UnsupportedEncodingException e) {
            throw new BizException(BizResultEnum.OTHER_UNSUPPORTED_ENCODING_ERROR, e);
        }
    }

    public static String getString(byte[] bytes) throws BizException {
        try {
            return new String(bytes, BizConstant.DEFAULT_CHARSET_NAME);
        } catch (UnsupportedEncodingException e) {
            throw new BizException(BizResultEnum.OTHER_UNSUPPORTED_ENCODING_ERROR, e);
        }
    }
}
